#include "stdafx.h"
#include "httpclient.h"
#include <getopt.h>
#include <fstream>
#include <istream>
#include <ostream>
#include <iostream>
#include <string>

#define FAILED_REASON_CONNECTION	1U
#define FAILED_REASON_RETURNCODE	2U
#define FAILED_REASON_MATCHTXT		3U


struct HTTPPROXY_APPCFG
{
	enum APP_MODE{ Mode_None=0, Mode_Host = 0x01, Mode_File=0x02, };
	UINT mode;
	std::string inputTxt;	//import file
	unsigned short port;	//import port

	std::string outputTxt;

	std::string host;		//inputTxt conflict with inputTxt

	std::string verifyHost;

	std::string verifyText;

	int debug;
};

static UINT64 GetTickNs()
{
	struct timeval tv = { 0 };
	gettimeofday(&tv, NULL);
	return (UINT64)tv.tv_sec * 1000000000 + (UINT64)tv.tv_usec * 1000;
}

static void showUseage()
{
	std::cout << "httpproxyc -- a program to verify a http proxy" << std::endl;
	std::cout << "version: 1.2" << std::endl << __DATE__ << std::endl;
}

static BOOL parseConfig(int argc, char* argv[], HTTPPROXY_APPCFG* pCfg)
{
	_ASSERT(pCfg);
	pCfg->debug = 0;
	pCfg->mode = HTTPPROXY_APPCFG::Mode_None;
	struct option appOptions[] =
	{
		{
			"ipfiles",//long option name
			required_argument,//no_argument,required_argument,optional_argument
			NULL,//flags,
			'i',//index,
		},
		{
			"debug",//long option name
			no_argument,//no_argument,required_argument,optional_argument
			NULL,//flags,
			'd',//index,
		},

		{
			"output",//long option name
			required_argument,//required_argument,optional_argument
			NULL,//flags,
			'o',//index,
		},
		{
			"help",
			no_argument,
			NULL,
			'h',
		},
		{
			"verify",
			required_argument,
			NULL,
			'y',
		},
		{
			"host",
			required_argument,
			NULL,
			't',
		},
		{
			"verifytext",
			required_argument,
			NULL,
			'x',
		},
		{
			"port",
			required_argument,
			NULL,
			'p',
		},
		{
			"thread",
			required_argument,
			NULL,
			'p',
		},
		{
			NULL,
			0,
			0,
			0,
		},
	};

	int opt = 0;
	int index = 0;

	while ((opt = getopt_long_only(argc, argv, "hi:o:t:p:y:x:", appOptions, &index)) != -1)
	{
		switch (opt)
		{
		case 'i':
			pCfg->inputTxt = optarg;
			pCfg->mode |= HTTPPROXY_APPCFG::Mode_File;
			break;

		case 'o':
			pCfg->outputTxt = optarg;
			break;

		case 'p':
			pCfg->port = atoi(optarg);
			break;

		case 0:
			printf("debug is %s\n", optarg);
			break;

		case '?':
			printf("invalid option %s", optarg);
			return FALSE;
			break;

		case 't':
			pCfg->host = optarg;
			pCfg->mode |= HTTPPROXY_APPCFG::Mode_Host;
			break;

		case 'h':
			showUseage();
			return FALSE;
			break;

		case 'y':
			pCfg->verifyHost = optarg;
			break;

		case 'd':
			pCfg->debug++;
			break;

		case 'x':
			pCfg->verifyText = optarg;
		}
	}
	if (pCfg->verifyText.size() == 0)
	{
		std::cerr << "NOT found verifyText,  program using verify host as default" << std::endl;
	}

	return pCfg->mode != HTTPPROXY_APPCFG::Mode_None;
}

int main(int argc, char* argv[])
{
	CHttpClient::Init();

	HTTPPROXY_APPCFG appcfg;
	appcfg.port = 0;
	if (!parseConfig(argc, argv, &appcfg))
	{
		goto __QUIT;
	}

	{
		CHttpClient client;
		HTTP_URI uri;
		uri.host = "ip138.com";
		uri.port = 80;
		if (appcfg.verifyHost.size() != 0)
		{
			uri.host = appcfg.verifyHost;
		}

		http_status_t httpcode = 0;
		HTTP_PROXY_CFG proxycfg;
		proxycfg.port = appcfg.port;
		proxycfg.connectTimeout = 3;

		if (appcfg.mode & HTTPPROXY_APPCFG::Mode_Host)
		{
			proxycfg.host = appcfg.host;
			UINT64 tStartNs = GetTickNs();

			CURLcode code = client.Get(uri, &httpcode, &proxycfg);

			UINT64 tEndNs = GetTickNs();
			std::string text = client.GetText();
			if (code != CURLE_OK)
			{
				std::cerr << proxycfg.host << " Failed(CONNECTION), cost " << tEndNs - tStartNs << " nanosenconds" << std::endl;
			}
			else if (httpcode != 200)
			{
				std::cerr << proxycfg.host << " Failed(HTTPRETURN), cost " << tEndNs - tStartNs << " nanosenconds" << std::endl;
			}
			else if (text.find(appcfg.verifyText) == std::string::npos)
			{
				std::cerr << proxycfg.host << " Failed(MATCHTXT), cost " << tEndNs - tStartNs << " nanosenconds" << std::endl;
			}
			if (appcfg.debug > 0)
			{
				std::cerr << "HTTP RETURED " << httpcode << std::endl;
			}
			else if (appcfg.debug > 1)
			{
				std::cerr << text << std::endl;
			}
			std::cerr << proxycfg.host << " OK, cost " << tEndNs - tStartNs << " nanosenconds" << std::endl;
		}

		if (appcfg.mode & HTTPPROXY_APPCFG::Mode_File)
		{
			std::ofstream fo(appcfg.outputTxt.c_str());
			std::ifstream fi(appcfg.inputTxt.c_str());
			std::streambuf* pOutBuf = NULL;
			if (!fi.is_open())
			{
				fprintf(stderr, "could not open input file %s\n", appcfg.inputTxt.c_str());
				goto __QUIT;
			}
			if (!fo.is_open())
			{
				fprintf(stderr, "could not open out file %s, using std::out instand\n", appcfg.outputTxt.c_str());
			}
			else
			{
				pOutBuf = std::cout.rdbuf(fo.rdbuf());
			}

			std::string line;
			while (getline(fi, line))
			{
				UINT nReason = 0;
				proxycfg.host = line;
				UINT64 tStartNs = GetTickNs();

				CURLcode code = client.Get(uri, &httpcode, &proxycfg);

				if (code == CURLE_OK)
				{
					UINT64 tEndNs = GetTickNs();
					std::string text = client.GetText();
					if (appcfg.debug > 0)
					{
						std::cerr << "HTTP RETURED " << httpcode << std::endl;
					}
					if (appcfg.debug > 1)
					{
						std::cerr << text << std::endl;
					}
					if (httpcode != 200)
					{
						nReason = FAILED_REASON_RETURNCODE;
						goto __VERIY_FAILED;
					}
					else if (text.find(appcfg.verifyText) == std::string::npos)
					{
						nReason = FAILED_REASON_MATCHTXT;
						goto __VERIY_FAILED;
					}
					std::cerr << line << " OK, cost " << tEndNs - tStartNs << " nanosenconds" << std::endl;
					std::cout << proxycfg.host << std::endl;
					continue;
				}
				else
				{
					nReason = FAILED_REASON_CONNECTION;
				}

				{
__VERIY_FAILED:
					UINT64 tEndNs = GetTickNs();
					std::cerr << line << " Failed( " << nReason << " ) cost "
						<< tEndNs - tStartNs << " nanosenconds" << std::endl;
				}
			}

			fi.close();
			if (fo.is_open())
			{
				std::cout.rdbuf(pOutBuf);
				fo.close();
			}
		}
	}

__QUIT:
	CHttpClient::UnInit();
	return 0;
}